package redis

import (
	"fmt"
	"strconv"

	"github.com/garyburd/redigo/redis"
	"github.com/rs/zerolog/log"
)

// ZADD 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
// 如果某个 member 已经是有序集的成员，那么更新这个 member 的 score 值，并通过重新插入这个 member 元素，来保证该 member 在正确的位置上。
// score 值可以是整数值或双精度浮点数。
// 如果 key 不存在，则创建一个空的有序集并执行 ZADD 操作。
// 当 key 存在但不是有序集类型时，返回一个错误。
//
// 使用例子:
//
//
//  scoreMember := map[int]string{111: "a", 101: "b"}
// 	if err := conn.ZADD("testZSET", scoreMember); err != nil {
// 		t.Error(err)
//  }
//
func (db *Conn) ZADD(key string, scoreMember interface{}) error {
	args := redis.Args{key}.AddFlat(scoreMember)
	fmt.Println(args)
	_, err := db.Conn.Do("ZADD", args...)
	return err
}

// ZSCORE 返回有序集 key 中，成员 member 的 score 值。
// 如果 member 元素不是有序集 key 的成员，或 key 不存在，返回 nil 。
func (db *Conn) ZSCORE(key string, member interface{}) string {
	score, err := redis.String(db.Conn.Do("ZSCORE", key, member))
	if err != nil {
		log.Error().Msgf("ZSCORE操作错误: %s, 操作key: %s, 操作member: %s", err, key, member)
	}
	return score
}

// ZINCRBY 为有序集 key 的成员 member 的 score 值加上增量 increment 。
// 可以通过传递一个负数值 increment ，让 score 减去相应的值，比如 ZINCRBY key -5 member ，就是让 member 的 score 值减去 5
// 当 key 不存在，或 member 不是 key 的成员时， ZINCRBY key increment member 等同于 ZADD key increment member 。
// 当 key 不是有序集类型时，返回一个错误。
// score 值可以是整数值或双精度浮点数。
func (db *Conn) ZINCRBY(key string, increment, member interface{}) string {
	score, err := redis.String(db.Conn.Do("ZINCRBY", key, increment, member))
	if err != nil {
		log.Error().Msgf("ZSCORE操作错误: %s, 操作key: %s, 操作member: %s,增量increment: %s", err, key, member, increment)
	}
	return score
}

// ZCARD 返回有序集 key 的基数(集合中元素的数量)。
// 当 key 存在且是有序集类型时，返回有序集的基数。 当 key 不存在时，返回 0 。
func (db *Conn) ZCARD(key string) int {
	score, err := redis.Int(db.Conn.Do("ZCARD", key))
	if err != nil {
		log.Error().Str("key", key).Msg("ZCARD操作错误")
	}
	return score
}

// ZCOUNT 返回有序集 key 中， score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。
// 返回值: score 值在 min 和 max 之间的成员的数量。
func (db *Conn) ZCOUNT(key string, min, max float64) int64 {
	score, err := redis.Int64(db.Conn.Do("ZCOUNT", key, min, max))
	if err != nil {
		log.Error().Str("key", key).Float64("min", min).Float64("max", max).Err(err).Msg("ZCARD操作错误")
	}
	return score
}

// ZRANGE 返回有序集 key 中，指定区间内的成员。
// 其中成员的位置按 score 值递增(从小到大)来排序。
// 具有相同 score 值的成员按字典序(lexicographical order )来排列。
// 下标参数 start 和 stop 都以 0 为底，也就是说，以 0 表示有序集第一个成员，以 1 表示有序集第二个成员，以此类推。
// 你也可以使用负数下标，以 -1 表示最后一个成员， -2 表示倒数第二个成员，以此类推。
// 超出范围的下标并不会引起错误。
// 比如说，当 start 的值比有序集的最大下标还要大，或是 start > stop 时， ZRANGE 命令只是简单地返回一个空列表。
// 另一方面，假如 stop 参数的值比有序集的最大下标还要大，那么 Redis 将 stop 当作最大下标来处理。
// 返回值: 指定区间内的有序集成员的列表。
func (db *Conn) ZRANGE(key string, start, stop int64) []string {
	result, err := redis.Strings(db.Conn.Do("ZRANGE", key, start, stop))
	if err != nil {
		log.Error().Str("key", key).Int64("start", start).Int64("stop", stop).Err(err).Msg("ZRANGE操作错误")
	}
	return result
}

// ZRANGEWITHSCORES 使用WITHSCORES选项的ZRANGE
func (db *Conn) ZRANGEWITHSCORES(key string, start, stop int64) []map[string]float64 {
	values, err := redis.Values(db.Conn.Do("ZRANGE", key, start, stop, "WITHSCORES"))
	if err != nil {
		log.Error().Str("key", key).Int64("start", start).Int64("stop", stop).Bool("WITHSCORES", true).Err(err).Msg("ZRANGE操作错误")
	}
	if len(values) == 0 {
		result := make([]map[string]float64, 0, 1)
		return result
	}
	length := len(values) / 2
	result := make([]map[string]float64, length, length)
	for i := 0; i < length; i++ {
		member := values[i*2].([]byte)
		m := string(member)
		score := values[i*2+1].([]byte)
		s, _ := strconv.ParseFloat(string(score), 64)
		item := map[string]float64{m: s}
		result[i] = item
	}
	return result
}

// ZREVRANGE 返回有序集 key 中，指定区间内的成员。
// 其中成员的位置按 score 值递减(从大到小)来排序。
// 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order)排列。
// 下标参数 start 和 stop 都以 0 为底，也就是说，以 0 表示有序集第一个成员，以 1 表示有序集第二个成员，以此类推。
// 你也可以使用负数下标，以 -1 表示最后一个成员， -2 表示倒数第二个成员，以此类推。
// 超出范围的下标并不会引起错误。
// 返回值: 指定区间内的有序集成员的列表。
func (db *Conn) ZREVRANGE(key string, start, stop int64) []string {
	result, err := redis.Strings(db.Conn.Do("ZREVRANGE", key, start, stop))
	if err != nil {
		log.Error().Str("key", key).Int64("start", start).Int64("stop", stop).Err(err).Msg("ZREVRANGE操作错误")
	}
	return result
}

// ZREVRANGEWITHSCORES 使用WITHSCORES选项的ZREVRANGE
func (db *Conn) ZREVRANGEWITHSCORES(key string, start, stop int64) []map[string]float64 {
	values, err := redis.Values(db.Conn.Do("ZREVRANGE", key, start, stop, "WITHSCORES"))
	if err != nil {
		log.Error().Str("key", key).Int64("start", start).Int64("stop", stop).Bool("WITHSCORES", true).Err(err).Msg("ZREVRANGE操作错误")
	}
	if len(values) == 0 {
		result := make([]map[string]float64, 0, 1)
		return result
	}
	length := len(values) / 2
	result := make([]map[string]float64, length, length)
	for i := 0; i < length; i++ {
		member := values[i*2].([]byte)
		m := string(member)
		score := values[i*2+1].([]byte)
		s, _ := strconv.ParseFloat(string(score), 64)
		item := map[string]float64{m: s}
		result[i] = item
	}
	return result
}

// ZRANK 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。
// 排名以 0 为底，也就是说， score 值最小的成员排名为 0 。
// 使用 ZREVRANK key member 命令可以获得成员按 score 值递减(从大到小)排列的排名。
// 返回值:
//  如果 member 是有序集 key 的成员，返回 member 的排名。
//  如果 member 不是有序集 key 的成员，返回 -1。
func (db *Conn) ZRANK(key string, member interface{}) int64 {
	rank, err := redis.Int64(db.Conn.Do("ZRANK", key, member))
	if err != nil {
		if err == redis.ErrNil {
			return -1
		}
		log.Error().Msgf("ZRANK操作错误: %s, 操作key: %s, 操作member: %s", err, key, member)
	}
	return rank
}

// ZREVRANK 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)顺序排列。
// 排名以 0 为底，也就是说， score 值最小的成员排名为 0 。
// 返回值:
//
//  * 如果 member 是有序集 key 的成员，返回 member 的排名。
//  * 如果 member 不是有序集 key 的成员，返回 -1。
//
func (db *Conn) ZREVRANK(key string, member interface{}) int64 {
	rank, err := redis.Int64(db.Conn.Do("ZREVRANK", key, member))
	if err != nil {
		if err == redis.ErrNil {
			return -1
		}
		log.Error().Msgf("ZREVRANK操作错误: %s, 操作key: %s, 操作member: %s", err, key, member)
	}
	return rank
}

// ZREM 移除有序集 key 中的一个或多个成员，不存在的成员将被忽略。
func (db *Conn) ZREM(key string, member ...interface{}) int64 {
	args := redis.Args{key}
	for _, field := range member {
		args = args.Add(field)
	}
	count, err := redis.Int64(db.Conn.Do("ZREM", args...))
	if err != nil {
		log.Error().Msgf("ZREM操作错误: %s, 操作key: %s, 操作member: %s", err, key, member)
	}
	return count
}
